# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/8/2 13:27
# @Author  : huidong.bai
# @File    : ClientSession.py
# @Software: PyCharm
# @Mail    : MasterBai2018@outlook.com
import json
import threading
from ctypes import *
from src.utils.common import method
from src.core.Status.AIBSSessionStatus import AIBSVRConfigCode
from conftest import logging
import allure


# 定义回调结构体
class AIBSResultST(Structure):
    _fields_ = [
        ("flag", c_int),                  # 回调音频能量时，表示data的长度
        ("data", c_char_p),               # 回调数据
        ("type", POINTER(c_char) * 128),  # 回调类型
    ]


class ClientSession:
    m_callback = None
    m_engine = None
    m_library = None
    m_lock = threading.Lock()

    def __init__(self, lib_path: str):
        try:
            self.m_library = cdll.LoadLibrary(lib_path)
        except Exception as e:
            logging.error(f"Load aibs client library error: {e}")

    # 实现callback回调函数功能，第一个参数c_int为返回值，result[0]为解引用
    @method(CFUNCTYPE(c_int, c_int, POINTER(AIBSResultST)))
    def callback_func(self, status, result):
        _data = result[0].data.decode(encoding="utf-8", errors='ignore')
        if _data is None or len(_data) == 0:
            return 0
        
        _type = create_string_buffer(128)
        memmove(_type, result[0].type, 128)
        real_callback_data = _type.value.decode(encoding="utf-8", errors='ignore')
        callback_data = json.loads(_data)
        with self.m_lock:
            self.notify_result(status, callback_data)
        return 0

    def set_listener(self, callback):
        self.m_callback = callback

    def notify_result(self, status, callback_data):
        if self.m_callback:
            self.m_callback(status, callback_data)

    def create(self, config: str, lang: str, appid: str) -> int:
        """
        Create接口
        :param config: 配置文件路径
        :param lang: 语种信息
        :return: 返回状态码: 0表示成功，其他表示异常
        :param appid: 应用appid
        """
        self.m_status = 0
        if (not lang) or (not config) or (not appid):
            logging.error(f"Create aibs client error, lang:{lang}, config:{config}, appid:{appid}")
            return -1

        logging.info(f'Create aibs client with conf: {config}')
        self.m_engine = self.m_library.aibs_create_engine(self.callback_func, None, config.encode('utf-8'), lang.encode('utf-8'), appid.encode('utf-8'))

        if self.m_engine is None:
            logging.error(f'Create aibs client engine failed, the engine is:{self.m_engine}')
            return -2
        return 0

    def set_engine_param(self, param: c_int, value) -> int:
        """
        设置引擎参数
        :param param: 参数类型，参考:AIBSParam
        :param value: 参数具体值
        :return: 引擎接口返回状态码
        """
        if type(value) == int:
            ret = self.m_library.set_aibs_param(self.m_engine, param, byref(c_uint(value)))
        elif type(value) == float:
            ret = self.m_library.set_aibs_param(self.m_engine, param, byref(c_float(value)))
        elif type(value) == str:
            ret = self.m_library.set_aibs_param(self.m_engine, param, c_char_p(value.encode('utf-8')))
        elif type(value) == bool:
            ret = self.m_library.set_aibs_param(self.m_engine, param, c_bool(value))
        else:
            logging.error(f'Set aibs engine parm error, unknown type of value: {type(value)}')
            return -1
        if ret != 0:
            logging.error(f'Set aibs engine parm error, ret: {ret}')
            return ret
        return 0

    def set_vr_reset(self):
        """
        恢复出厂设置
        :return: 返回状态码，0表示成功，其他表示失败
        """
        return self.m_library.aibs_clear_vr_config(self.m_engine)

    def set_vr_config(self, param: c_int, value):
        """
        设置VR配置项
        :param param: 参数类型，参考:AIBSVRConfigCode
        :param value: 参数具体值
        :return: 设置VR配置项接口返回状态码
        """
        if type(value) == int:
            ret = self.m_library.aibs_set_vr_config(self.m_engine, param, byref(c_uint(value)))
        elif type(value) == dict:
            json_data = json.dumps(value, ensure_ascii=False, separators=(',', ':'))
            ret = self.m_library.aibs_set_vr_config(self.m_engine, param, c_char_p(json_data.encode('utf-8')))
        else:
            logging.error(f'Set vr config parm error, unknown type of value: {type(value)}')
            return -1
        if ret != 0:
            logging.error(f'Set vr config parm error, ret: {ret}')
            return ret
        return 0

    def get_vr_config(self, key):
        """
        根据传入的配置项获取对应的值
        :return: 返回状态码，0表示成功，其他表示失败
        """
        self.m_library.aibs_get_vr_config.restype = c_char_p
        address = self.m_library.aibs_get_vr_config(self.m_engine, key)
        if address:
            if key == AIBSVRConfigCode.AIBS_SETTING_GET_WAKEUP_WORD:
                return address.decode(encoding='utf-8')
            return address.decode(encoding='unicode_escape')
        else:
            return None

    def set_wakeup_word(self, word: str, threshold: float) -> int:
        """
        设置自定义唤醒词接口
        :param word: 自定义唤醒词
        :param threshold: 阈值
        :return: 状态码，0表示成功，其他表示失败
        """
        return self.m_library.aibs_set_wakeup_word(self.m_engine, word.encode(), c_float(threshold))

    def set_wakeup_word_enable(self, word: str, option) -> int:
        return self.m_library.aibs_set_wakeup_word_enable(self.m_engine, word.encode(), c_bool(option))

    def get_wakeup_word_list(self):
        """
        获取唤醒词列表
        :return: 成功则返回wakeup word，否则返回NULL
        """
        result = []
        self.m_library.aibs_get_wakeup_word.restype = c_char_p
        ref = self.m_library.aibs_get_wakeup_word(self.m_engine)
        try:
            data = ref.decode(encoding="utf-8")
            wakeup_list = json.loads(data).get("wakeup").get("data").get("fix_data")
            for i in wakeup_list:
                result.append(i.get("word"))
            return result
        except Exception as e:
            logging.error(f"Get wakeup list error:{e}")
            return []

    def set_event(self, event) -> int:
        json_data = None
        try:
            json_data = json.dumps(event, ensure_ascii=False)
        except Exception as e:
            logging.error(f"Set event error, parse json data error:{e}")

        with allure.step(f"发送事件：{event.get('type')}"):
            with allure.step(f"{event}"): pass
        return self.m_library.aibs_set_event(self.m_engine, json_data.encode('utf-8'))

    def set_vr_event(self, event) -> int:
        json_data = None
        try:
            json_data = json.dumps(event, ensure_ascii=False)
        except Exception as e:
            logging.error(f"Set vr event error, parse json data error:{e}")

        with allure.step(f"发送事件：{event.get('type')}"):
            with allure.step(f"{event}"): pass
        return self.m_library.aibs_set_vr_event(self.m_engine, json_data.encode('utf-8'))

    def start_speaker_enroll(self, user_id, text, id, channel_id):
        self.m_library.aibs_start_speaker_enroll.argtypes = [c_void_p, c_char_p, c_char_p, c_int, c_int]
        self.m_library.aibs_start_speaker_enroll.restype = c_int
        return self.m_library.aibs_start_speaker_enroll(self.m_engine, user_id.encode(), text.encode(), id, channel_id)

    def end_speaker_enroll(self):
        self.m_library.aibs_end_speaker_enroll.argtypes = [c_void_p]
        self.m_library.aibs_end_speaker_enroll.restype = c_int
        return self.m_library.aibs_end_speaker_enroll(self.m_engine)

    def recognize_speaker(self, channel_id, start_time, end_time):
        self.m_library.aibs_recognize_speaker.argtypes = [c_void_p, c_int, c_long, c_long]
        self.m_library.aibs_recognize_speaker.restype = c_char_p
        result = self.m_library.aibs_recognize_speaker(self.m_engine, channel_id, start_time, end_time)
        if result:
            json_data = result.decode(encoding="utf-8")
            user_id = json.loads(json_data).get("data").get("user_id")
            return user_id
        else:
            return None

    def delete_speaker_info(self, user_id):
        self.m_library.aibs_delete_speaker_info.argtypes = [c_void_p, c_char_p]
        self.m_library.aibs_delete_speaker_info.restype = c_int
        return self.m_library.aibs_delete_speaker_info(self.m_engine, user_id.encode())

    def get_speaker_info(self, user_id):
        self.m_library.aibs_get_speaker_info.argtypes = [c_void_p, c_char_p]
        self.m_library.aibs_get_speaker_info.restype = c_char_p
        result = self.m_library.aibs_get_speaker_info(self.m_engine, user_id.encode())
        if result:
            json_data = result.decode(encoding="utf-8")
            user_id = json.loads(json_data).get("data").get("user_id")
            return user_id
        else:
            return None

    def get_enroll_text(self):
        self.m_library.aibs_get_enroll_text.argtypes = [c_void_p]
        self.m_library.aibs_get_enroll_text.restype = c_char_p
        result =  self.m_library.aibs_get_enroll_text(self.m_engine)
        return result.decode(encoding="utf-8")

    def aibs_sensitive_word_judgment(self, word):
        self.m_library.aibs_sensitive_word_judgment.argtypes = [c_void_p, c_char_p]
        self.m_library.aibs_sensitive_word_judgment.restype = c_int
        result =  self.m_library.aibs_sensitive_word_judgment(self.m_engine, word.encode())
        return result

    def aibs_get_registered_speaker(self):
        self.m_library.aibs_get_registered_speaker.argtypes = [c_void_p]
        self.m_library.aibs_get_registered_speaker.restype = c_char_p
        result =  self.m_library.aibs_get_registered_speaker(self.m_engine)
        return result.decode(encoding="utf-8")

    def open_voice_input(self, param):
        json_param = None
        try:
            json_param = json.dumps(param, ensure_ascii=False)
        except Exception as e:
            logging.error(f"Set event error, parse json data error:{e}")

        self.m_library.aibs_open_voice_input.argtypes = [c_void_p, c_char_p]
        self.m_library.aibs_open_voice_input.restype = c_int
        return self.m_library.aibs_open_voice_input(self.m_engine, json_param.encode())

    def close_voice_input(self):
        self.m_library.aibs_close_voice_input.argtypes = [c_void_p]
        self.m_library.aibs_close_voice_input.restype = c_int
        return self.m_library.aibs_close_voice_input(self.m_engine)

    def set_link_type(self, linkType):
        self.m_library.aibs_set_link_type.argtypes = [c_void_p, c_int]
        self.m_library.aibs_set_link_type.restype = c_int
        return self.m_library.aibs_set_link_type(self.m_engine, c_int(linkType))
